﻿using System;   
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Xml;

namespace Clandestine
{
    public struct SpritesheetSelection
    {
        public int RowStart;
        public int RowEnd;
        public int ColStart;
        public int ColEnd;
        public string Name;
        public double Interval;

        public SpritesheetSelection(string name, double interval, int rowStart, int colStart, int rowEnd, int colEnd)
        {
            this.Name = name;
            this.RowStart = rowStart;
            this.RowEnd = rowEnd;
            this.ColStart = colStart;
            this.ColEnd = colEnd;
            this.Interval = interval;
        }
        public SpritesheetSelection(string name, int rowStart, int colStart, int rowEnd, int colEnd)
        {
            this.Name = name;
            this.RowStart = rowStart;
            this.RowEnd = rowEnd;
            this.ColStart = colStart;
            this.ColEnd = colEnd;
            this.Interval = 0;
        }
    }

    public class AnimatedSprite : Sprite
    {
        private Dictionary<string, Animation> Animations = new Dictionary<string, Animation>();

        public Animation this[string name]
        {
            get
            {
                if (Animations.ContainsKey(name))
                    return Animations[name];
                else
                {
                    Log.e("Animations: Tried to get animation with name, \"{0}\", but the AnimatedSprite contains no such animation.", name);
                    throw new ArgumentException(); // Else we have to return something..
                }
            }
        }

        private Animation currentAnimation;
        public Animation CurrentAnimation
        {
            get { return currentAnimation; }
        }

        public int CurrentFrame
        {
            get { return currentAnimation.CurrentFrame; }
            set { currentAnimation.CurrentFrame = value; }
        }

        public bool Playing
        {
            get { return currentAnimation.Playing; }
            set 
            {
                if (currentAnimation != null)
                    currentAnimation.Playing = value; 
            }
        }

        public double Interval
        {
            get { return currentAnimation.Interval; }
            set { currentAnimation.Interval = value; }
        }

        public int FrameCount
        {
            get { return currentAnimation.FrameCount; }
        }

        protected override Texture Texture
        {
            get 
            {
                if (currentAnimation != null)
                    return currentAnimation.Frames[CurrentAnimation.CurrentFrame];
                else
                    return Texture.GetTexture("error.png");
            }
            // I don't think we want set { }
        }

        public AnimatedSprite()
        {
        }
        public AnimatedSprite(string spritesheet, string metadata)
            : this()
        {
            this.AddAnimations(spritesheet, metadata);
        }
        public AnimatedSprite(string spritesheet, int gridWidth, int gridHeight, SpritesheetSelection[] animations) 
            : this()
        {
            this.AddAnimations(spritesheet, gridWidth, gridHeight, animations);
        }

        public void StartAndWait()
        {
            currentAnimation.Playing = true;
            currentAnimation.FinishedAnimation.WaitOne();
        }

        public void Start()
        {
            currentAnimation.Playing = true;
        }
        public void Start(double interval)
        {
            Interval = interval;
            Start();
        }
        public void Start(string name)
        {
            SetAnimation(name);
            Start();
        }
        public void Start(string name, double interval)
        {
            Interval = interval;
            SetAnimation(name);
            Start();
        }

        public void Stop()
        {
            currentAnimation.Playing = false;
        }

        public void AddAnimations(string spritesheet, int gridWidth, int gridHeight, SpritesheetSelection[] animations)
        {
            Bitmap bmp = new Bitmap(spritesheet);
            foreach (SpritesheetSelection s in animations)
            {
                Animation a = new Animation(spritesheet, bmp, gridWidth, gridHeight, s.RowStart, s.ColStart, s.RowEnd, s.ColEnd);
                if (s.Interval > 0)
                    a.Interval = s.Interval;
                this.AddAnimation(s.Name, a);
            }
        }

        public void AddAnimations(string spritesheet, string metadata)
        {
            XmlTextReader xml = new XmlTextReader(metadata);
            Bitmap bmp = new Bitmap(spritesheet);
            int gridWidth = 1;
            int gridHeight = 1;

            // Read that sexy metadata
            while (xml.Read())
            {
                // General stuff
                if ((xml.NodeType == XmlNodeType.Element) && (xml.Name == "animations"))
                {
                    gridWidth = int.Parse(xml.GetAttribute("gridWidth"));
                    gridHeight = int.Parse(xml.GetAttribute("gridHeight"));
                }

                // Animation
                if ((xml.NodeType == XmlNodeType.Element) && (xml.Name == "animation"))
                {
                    string name = xml.GetAttribute("name");
                    double interval = double.Parse(xml.GetAttribute("interval")); // I bet these throw exceptions one day. Have fun!
                    bool loop = bool.Parse(xml.GetAttribute("loop"));
                    Animation a = new Animation(name, interval, loop);

                    // Add those frames!
                    while (!((xml.NodeType == XmlNodeType.EndElement) && (xml.Name == "animation")) && xml.Read())
                        if ((xml.NodeType == XmlNodeType.Element) && (xml.Name == "frames"))
                        {
                            int rowStart = int.Parse(xml.GetAttribute("rowStart"));
                            int colStart = int.Parse(xml.GetAttribute("colStart"));
                            int rowEnd = int.Parse(xml.GetAttribute("rowEnd"));
                            int colEnd = int.Parse(xml.GetAttribute("colEnd"));
                            a.AddFrames(spritesheet, bmp, gridWidth, gridHeight, rowStart, colStart, rowEnd, colEnd);
                        }

                    this.AddAnimation(a.Name, a);
                }
            }
            xml.Close();
        }

        public void AddAnimation(string name, Animation animation)
        {
            if (!Animations.ContainsKey(name))
            {
                Animations.Add(name, animation);
                //if (animation.Name != string.Empty)
                //    Log.w("Animation: We are renaming animation, \"{0}\" to \"{1}\", this could cause problems in whatever AnimatedSprite it is currently a member of.", animation.Name, name);
                animation.Name = name;  
                if (currentAnimation == null)
                    SetAnimation(name);
            }
            else
                Log.e("Animation: Cannot add animation, \"{0}\", to animated sprite as an animation with that name already exists.", name);
        }

        public void RemoveAnimation(string name)
        {
            if (Animations.ContainsKey(name))
                Animations.Remove(name);
            else
                Log.w("Animation: tried to delete animation, \"{0}\", from an animated sprite but no animation with that name existed.", name);
        }

        public void RemoveAnimation(Animation animation)
        {
            if (Animations.ContainsKey(animation.Name))
                Animations.Remove(animation.Name);
            else
                Log.w("Animation: Tried to delete animation an animation, from an animated sprite but no animation with that name existed.");
        }

        public void ClearAnimations()
        {
            Animations.Clear();
            Log.i("Animation: Clearing an animated sprite..."); // This could be more useful if the class had a .Name
        }

        public void SetAnimation(string name)
        {
            if (Animations.ContainsKey(name))
            {
                bool playing = false;
                if (currentAnimation != null) // may be first animation
                {
                    playing = currentAnimation.Playing;
                    currentAnimation.Playing = false; // Disable it's tmr!
                }
                currentAnimation = Animations[name];
                currentAnimation.Playing = playing;
            }
            else
                Log.e("Animation: Tried to set animation to, \"{0}\", but no such animation exists in here.", name);
        }

        public override void Dispose()
        {
            base.Dispose();
            foreach (KeyValuePair<string, Animation> kvp in Animations)
                kvp.Value.Dispose();
        }
    }

    
}
